# 下載最新的 Anaconda https://www.anaconda.com/products/individual # 所有的版本 https://repo.anaconda.com/archive/ # Sublime Text 文字編輯器下載 https://www.sublimetext.com/download ### 3-1 ### # 建立一個 Streamlit 專用的 Python 3.13 虛擬環境,名稱為 streamlit_313 >>> conda create --name streamlit_313 python=3.13 # 啟動虛擬環境 streamlit_313 >>> activate streamlit_313 # 安裝 torch, torchvision, streamlit (streamlit_313) >>> pip install torch (or pip install torch==2.4.1) (streamlit_313) >>> pip install torchvision (or pip install torchvision==0.19.1) (streamlit_313) >>> pip install streamlit (or pip install streamlit==1.42.0) ### 3-2 (app.py) ### import streamlit as st # 匯入 Streamlit 套件 st.title('我的第一個 Streamlit 應用程式') # 設定應用程式的標題 name = st.text_input('請輸入你的名字:') # 建立一個文字輸入欄位,讓使用者輸入名字 # 如果使用者有輸入名字,就顯示問候語和數字輸入欄位 if name: st.write(f'你好,{name}!') # 顯示使用者的名字 number = st.number_input('請輸入一個數字:', min_value = 0, max_value = 100) # 建立一個數字輸入欄位,限制範圍為 0 到 100 st.write('你輸入的數字是:', number) # 顯示使用者輸入的數字 ### 3-3 ### # 切換到 app.py 所在的路徑,使用 streamlit run 命令執行 App1.py (streamlit_313) >>> cd ch03 (streamlit_313) >>> streamlit run App1.py ### 3-4 ### # 加上 --server.port 參數,指定 port 8888 (streamlit_313) >>> streamlit run App1.py --server.port 8888 ### 3-5 (runApp1.py) ### import os py_file = 'App1.py' port = 8888 def run_streamlit_app(): command = f'streamlit run {py_file} --server.port {port}' # 建立 Streamlit 應用的命令 os.system(command) # 執行 streamlit run 命令 if __name__ == '__main__': run_streamlit_app() ### 3-6 ### # 執行 runApp1.py 較為方便 (streamlit_313) >>> python runApp1.py ### 3-7 (3-1.py) ### import streamlit as st st.title('我的 Streamlit 應用程式') # 顯示主標題文字 st.subheader('YOLO 物體偵測介面') # 顯示子標題文字 name = 'YOLO' # 定義變數 name,值為字串 'YOLO' st.write(name) # 顯示變數 name 的內容,也就是顯示 'YOLO' st.write('建立 **DataFrame 資料表格**:') # 顯示文字,並加粗 DataFrame 資料表格部分 (markdown 語法) ### 3-8 (3-2.py) ### import streamlit as st # 匯入 Streamlit,用來建立網頁應用程式介面 import pandas as pd # 匯入 Pandas,用來建立和操作資料表格 ... 略 ... # 建立一個 Pandas 資料框 DataFrame df = pd.DataFrame({ '索引': [15, 16, 17, 18], # 索引欄位資料 '值': [100, 200, 300, 400] # 對應的值欄位資料 }) # 顯示資料表格,使用 dataframe(可互動調整大小) # width = 'stretch' 表示表格的寬度會根據容器的寬度自動拉伸 # height = 400 設定表格的高度為 400 像素 st.dataframe(df, width = 'stretch', height = 400) name # 再次顯示變數 name 的內容(這一行其實不會有輸出,因為沒有用 st.write()) ### 3-9 (3-3.py) ### import streamlit as st import pandas as pd import numpy as np # 匯入 NumPy,用來產生數值資料 ... 略 ... # 建立一組隨機資料用於繪圖(30 筆資料,每筆有 A、B、C 三個欄位) chart_data = pd.DataFrame( np.random.randn(30, 3), # 產生 30x3 的標準常態分布隨機數據 columns = ['A', 'B', 'C'] # 欄位名稱 ) st.line_chart(chart_data) # 畫出折線圖(Line Chart),將上述的隨機資料視覺化 ### 3-10 (3-4.py) ### import streamlit as st import pandas as pd import numpy as np ... 略 ... # 建立隨機的 30 筆地圖座標資料 # 經緯度分別加入 [22.6, 120.4] 表示以台灣南部為中心 map_data = pd.DataFrame( np.random.randn(30, 2) / [50, 50] + [22.6, 120.4], # 調整數據範圍與中心位置 columns=['lat', 'lon'] # 欄位名稱:緯度 (lat)、經度 (lon) ) st.map(map_data) # 顯示地圖,並在圖上標示出隨機生成的位置點 ### 3-11 (3-5.py) ### import streamlit as st import pandas as pd import numpy as np ... 略 ... # 判斷是否按下按鈕 if st.button('請按我'): # 這行會顯示一個標題為「請按我」的按鈕 st.text('你已經按下此按鈕!') # 當按鈕被點擊時,顯示文字訊息「你已經按下此按鈕!」 ### 3-12 (3-6.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 建立一個勾選框,讓使用者選擇是否顯示圖表 if st.checkbox('顯示圖像'): # 當使用者勾選了「顯示圖像」勾選框時,執行以下程式碼 # 使用 numpy 生成一個 30x3 的隨機數字矩陣,並將其轉換為 pandas DataFrame 格式 chart_data = pd.DataFrame( np.random.randn(30, 3), # 產生 30x3 的標準常態分布隨機數據 columns = ['A', 'B', 'columns'] # 欄位名稱 ) # 顯示這些隨機數據的折線圖 st.line_chart(chart_data) ### 3-13 (3-7.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 顯示一個選擇框,讓使用者選擇他最喜歡的電腦視覺套件 option = st.selectbox( '你最喜歡的電腦視覺套件?', # 標題,詢問使用者最喜歡的電腦視覺套件 ['OpenCV', 'MediaPipe/CVZone', 'Dlib', 'YOLO'] # 可選擇的選項列表 (串列容器) ) # 顯示使用者的選擇 st.text('你的選擇: ' + option) # 顯示文字「你的選擇: 」並加上使用者所選的選項 ### 3-14 (3-8.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 顯示滑桿讓使用者選擇信心指數,範圍從 30 到 100,預設值為 50 confidence = int(st.slider('選擇信心指數', 30, 100, 50)) # 顯示使用者選擇的信心指數的百分比(轉換為小數形式) st.write(confidence / 100.0) ### 3-15 (3-9.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 使用 with 區塊來創建表單,key 為 'register_form' with st.form(key = 'register_form'): name = st.text_input(label = '姓名', placeholder = '請輸入姓名') # 顯示姓名輸入框,並提供提示文字「請輸入姓名」 gender = st.selectbox('性別', ['男', '女']) # 顯示性別選擇框,選項有「男」和「女」 birthday = st.date_input('生日') # 顯示生日選擇框 submit_btn = st.form_submit_button(label = '送出') # 顯示送出按鈕,按下後會提交表單 if submit_btn: # 當按下「送出」按鈕後,顯示註冊資料 st.write(f'註冊資料: {name}, 性別: {gender}, 生日: {birthday}') ### 3-16 (3-10.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 在側邊欄(sidebar)建立一個滑動條(slider) # 使用者可以在側邊欄中選擇 “信心指數”(confidence value),範圍從 30 到 100,預設為 50 # key='conf' 是這個 widget 的唯一識別符號,用於維持狀態 confidence = int(st.sidebar.slider('選擇信心指數', 30, 100, 50, key = 'conf')) # 將使用者所選的信心指數除以 100,轉為 0.x 形式顯示在側邊欄中 st.sidebar.write(confidence / 100.0) ### 3-17 (3-11.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # st.columns(3) 建立三個並排的欄位(左右三欄平分空間) column1, column2, column3 = st.columns(3) column1.write('第一欄') # 在第一個欄位中寫入文字「第一欄」 column1.write(10) # 在第一個欄位中,繼續寫入數值 10 column2.write('第二欄') column2.write(20) column3.write('第三欄') column3.write(30) ### 3-18 (3-12.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') expander = st.expander('請點擊展開...') # 建立一個可展開/收合的區塊 (expander),標籤為 「請點擊展開...」 # 在 expander 裡寫入一些內容 # 使用 expander.write() 將文字「展開顯示更多內容...」顯示在這個可展開區塊裡 expander.write('展開顯示更多內容...') ### 3-19 (3-13.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 使用 st.tabs 建立兩個分頁(tab),標籤分別為 '一隻貓' 與 '一隻狗' tab1, tab2 = st.tabs(['一隻貓', '一隻狗']) with tab1: # 進入第一個分頁 '一隻貓' st.header('一隻貓') # 在 '一隻貓' 分頁中顯示副標題 '一隻貓' st.image('images/cat.jpg', width = 200) # 顯示一張貓的圖片,寬度設定為 200 像素 with tab2: # 進入第二個分頁 '一隻狗' st.header('一隻狗') st.image('images/dog.jpg', width = 200) ### 3-20 (3-14.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') if st.button('開始計數'): # 建立一個按鈕,文字為「開始計數」。當使用者按下這個按鈕時,下面的程式區塊會執行。 bar = st.progress(0) # 建立一個進度條,初始值設為 0%。這會在頁面上顯示一條空進度條。 for i in range(100): # 進入一個從 0 到 99 的迴圈,共 100 次,用來模擬計數或某種逐步執行的工作。 bar.progress(i + 1, f'目前進度: {i + 1} %') # 更新進度條為第 i+1 個百分比(1% 到 100%),並顯示狀態文字「目前進度: X %」 time.sleep(0.05) # 暫停 0.05 秒,讓進度條的變化可以被看見,模擬某些工作在進行(如下載或處理資料) bar.progress(100, '載入完成...') # 當迴圈結束後,確保進度條達 100%,並顯示文字「載入完成...」 ### 3-21 (3-15.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') if st.button('儲存資料', type='primary'): # 建立一個按鈕,文字為 '儲存資料',type = 'primary' 表示這個按鈕是主要型按鈕(樣式比較強調) st.toast('編輯內容已經成功儲存...') # 顯示一條 'toast' 通知訊息給使用者 ### 3-22 (3-16.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 如果按下按鈕,就顯示不同種類的訊息框 if st.button('顯示訊息框', type='secondary'): st.success('成功...') # 顯示綠色成功訊息 st.info('資訊...') # 顯示藍色資訊訊息 st.warning('警告...') # 顯示黃色警告訊息 st.error('錯誤...') # 顯示紅色錯誤訊息 ### 3-23 (3-17.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 建立一個按鈕,當使用者點擊時會顯示網頁特效 if st.button('顯示網頁特效'): st.balloons() # 顯示彩色氣球動畫(慶祝效果) st.snow() # 顯示飄雪動畫(有趣的視覺效果 ### 3-24 (3-18.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') # 模擬使用者的聊天訊息區塊 with st.chat_message('user'): st.write('Hi! 請問你是誰') # 顯示使用者的訊息 # 模擬助理的聊天訊息區塊 message = st.chat_message('assistant') # 建立助理訊息物件 message.write('你好! LLM 可以回答你各種問題...') # 助理回應的第一段訊息 message.write('請問我有什麼可以幫助你的嗎?') # 助理回應的第二段訊息 ### 3-25 (3-19.py) ### import streamlit as st import pandas as pd import numpy as np st.title('我的 Streamlit 應用程式') st.subheader('YOLO 物體偵測介面') with st.chat_message('user'): st.write('Hi! 請問你是誰') message = st.chat_message('assistant') message.write('你好! LLM 可以回答你各種問題...') message.write('請問我有什麼可以幫助你的嗎?') # 顯示一個聊天輸入框,提示使用者輸入訊息 user_input = st.chat_input('請輸入聊天訊息...?') # 如果使用者有輸入訊息(也就是 user_input 不是 None) if user_input: # 在聊天室中以「使用者」的身分顯示剛剛輸入的內容 with st.chat_message('user'): st.write(user_input) # 接著由「助理」回應使用者的訊息 message = st.chat_message('assistant') message.write('你剛剛說的是:' + user_input) ### 3-26 (App2.py) ### import streamlit as st import numpy as np import pandas as pd import time # 設定 Streamlit 網頁的基本配置 # # 3-7-2 st.set_page_config( page_title = '我的Streamlit應用程式', # 設定網頁標題 page_icon = 'random', # 設定網頁的圖示,可以是 emoji 或自訂圖示(此處為 'random' 表示隨機) layout = 'centered', # 設定頁面版面配置(centered 為置中顯示,wide 為寬版) initial_sidebar_state = 'expanded', # 側邊欄初始狀態,'expanded' 表示展開;也可設為 'collapsed' menu_items = { # 自訂右上角選單項目 'Get Help': 'https://fchart.github.io/', # 使用者點擊 "Get Help" 時跳轉的網址 'About': '**[fChart](https://fchart.github.io/)** 是fChart工具的網頁' # 關於資訊,可使用 Markdown 格式 } ) ... 略 ... # 使用 Streamlit 的快取功能來加速資料讀取 # 3-7-1 @st.cache_data(ttl = 3600, show_spinner = '正在載入資料...') def load_csv_data(url): # 從指定的 URL 讀取 CSV 資料,並回傳為 Pandas 的 DataFrame return pd.read_csv(url) # 呼叫函式來載入資料,並將其存入 df 變數 df = load_csv_data('https://raw.githubusercontent.com/fchart/PythonCV/refs/heads/main/26k-consumer-complaints.csv') # df = load_csv_data('data/26k-consumer-complaints.csv') # 使用 Streamlit 的 dataframe 元件顯示資料表格 st.dataframe(df) ### 3-27 ### (streamlit_313) >>> pip install ultralytics (streamlit_313) >>> pip install opencv-python (streamlit_313) >>> python run3-20-3.py ### 3-28 (3-20-1.py) ### import streamlit as st st.title('上傳圖檔執行YOLO物體偵測') # 建立檔案上傳器,讓使用者上傳圖片檔案 source_img = st.file_uploader( label = '選擇圖檔...', # 上傳器的提示文字 type = ('jpg', 'jpeg', 'png', 'bmp', 'webp') # 限定可接受的圖檔類型 ) ### 3-29 (3-20-2.py) ### import streamlit as st from PIL import Image from ultralytics import YOLO model = YOLO('yolo12n.pt') st.title('上傳圖檔執行YOLO物體偵測') source_img = st.file_uploader( label = '選擇圖檔...', type = ('jpg', 'jpeg', 'png', 'bmp', 'webp') ) # 建立兩欄位佈局,用來左右並排顯示「原圖」與「偵測結果」 col1, col2 = st.columns(2) # 左欄位:顯示使用者上傳的原始圖片 with col1: if source_img: # 使用 PIL 開啟上傳的圖片 uploaded_image = Image.open(source_img) # 顯示圖片 st.image(image = source_img, caption = '上傳圖檔', use_container_width = True) # 如果有上傳圖片,就顯示「執行」按鈕 if source_img: if st.button('執行'): # 顯示載入動畫,提示使用者正在執行中 with st.spinner('執行中...'): # 使用 YOLO 模型對圖片進行預測(conf 是信心閾值,預測分數需 > 0.6 才顯示) res = model.predict(uploaded_image, conf = 0.6) # res[0].plot() 會將偵測結果畫在圖片上,回傳 numpy 陣列(BGR) res_plotted = res[0].plot()[:, :, ::-1] # 將 BGR 轉為 RGB 顯示 # 右欄位:顯示畫上偵測框的圖片結果 with col2: st.image(res_plotted, caption = 'YOLO 偵測結果', use_container_width = True) ### 3-30 (3-20-3.py) ### import streamlit as st from PIL import Image from ultralytics import YOLO model = YOLO('yolo12n.pt') st.title('上傳圖檔執行YOLO物體偵測') source_img = st.file_uploader( label = '選擇圖檔...', type = ('jpg', 'jpeg', 'png', 'bmp', 'webp') ) # 建立兩欄位佈局,用來左右並排顯示「原圖」與「偵測結果」 col1, col2 = st.columns(2) # 左欄位:顯示使用者上傳的原始圖片 with col1: if source_img: # 使用 PIL 開啟上傳的圖片 uploaded_image = Image.open(source_img) # 顯示圖片 st.image(image = source_img, caption = '上傳圖檔', use_container_width = True) # 檢查是否已有上傳圖片 if source_img: # 若使用者按下「執行」按鈕 if st.button('執行'): # 顯示載入中動畫,提示使用者模型正在執行 with st.spinner('執行中...'): # 使用 YOLO 模型進行預測,conf=0.6 為信心門檻(60%以上才顯示) res = model.predict(uploaded_image, conf = 0.6) # 將預測結果繪製在圖片上,並轉換 BGR 為 RGB(符合 Streamlit 顯示格式) res_plotted = res[0].plot()[:, :, ::-1] # 顯示右邊欄位中的預測圖片 with col2: st.image(res_plotted, caption = '偵測結果影像', use_container_width = True) # 嘗試展開詳細資訊的區塊 try: # 使用 expander 顯示偵測框資料(可展開/收合) with st.expander('偵測結果'): # 逐一讀取偵測結果中的每個「邊界框」 for box in res[0].boxes: # 取得邊界框的 xyxy 座標(左上角 x, y;右下角 x, y) cords = box.xyxy[0].tolist() cords = [round(x) for x in cords] # 四捨五入為整數 # 取得類別 ID(例如:0 表示 'person',1 表示 'car' 等) class_id = int(box.cls[0].item()) # 取得信心分數(轉為百分比顯示) conf = box.conf[0].item() conf = round(conf * 100, 2) # 顯示詳細資訊 st.write('分類編號:', class_id) st.write('分類名稱:', model.names[class_id]) st.write('邊界框座標:', cords) st.write('信心指數:', conf, '%') st.write('------------------------') ### 3-31 (3-21-1.py) ### import streamlit as st import tempfile # tempfile:用來建立臨時檔案(處理上傳影片) st.title('上傳影片檔執行YOLO物體偵測') # 建立影片檔上傳器 source_video = st.file_uploader(label = '選擇上傳影片檔...') # 如果使用者有上傳影片 if source_video: # 顯示上傳的影片(僅播放,不進行推論) st.video(source_video) # 顯示「執行」按鈕,點下去後進行處理 if st.button('執行'): with st.spinner('執行中...'): try: # 建立一個臨時檔案,用來儲存上傳的影片內容 tfile = tempfile.NamedTemporaryFile() tfile.write(source_video.read()) # 使用 OpenCV 開啟影片檔(這裡尚未匯入 cv2) vid_cap = cv2.VideoCapture(tfile.name) except Exception as e: # 若有錯誤,顯示錯誤訊息 st.error(f'載入影片檔錯誤: {e}') ### 3-32 (3-21-2.py) ### import streamlit as st import cv2 from ultralytics import YOLO import tempfile model = YOLO('yolo12n.pt') # 自訂函式:顯示 YOLO 偵測結果的影片影格 def _display_detected_frames(conf, model, st_frame, image): # 調整圖片大小(寬 720 px,保持 16:9 比例) image = cv2.resize(image, (720, int(720 * (9 / 16)))) # 執行 YOLO 模型預測(物件偵測) res = model.predict(image, conf = conf) # 將預測結果畫上邊界框等資訊,回傳圖像(格式為 BGR) res_plotted = res[0].plot() # 使用 Streamlit 顯示影格 st_frame.image(res_plotted, caption = '偵測結果影片', channels = 'BGR', # OpenCV 預設是 BGR,所以這裡要指定 width = 'stretch') # stretch 讓圖片自動撐滿欄位 st.title('上傳影片檔執行YOLO物體偵測') source_video = st.file_uploader(label = '選擇上傳影片檔...') # 如果使用者有上傳影片檔 if source_video: # 在頁面上播放原始影片 st.video(source_video) # 按下「執行」按鈕後開始物體偵測 if st.button('執行'): with st.spinner('執行中...'): # 顯示載入動畫 try: # 將上傳的影片暫存為本機臨時檔案 tfile = tempfile.NamedTemporaryFile() tfile.write(source_video.read()) # 使用 OpenCV 開啟影片 vid_cap = cv2.VideoCapture(tfile.name) # 建立一個空白容器來即時更新顯示每一幀的 YOLO 偵測結果 st_frame = st.empty() # 使用 while 迴圈逐幀讀取影片內容 while vid_cap.isOpened(): success, image = vid_cap.read() # 嘗試讀取一幀影像 if success: # 對成功讀取的每一幀進行 YOLO 偵測與顯示 _display_detected_frames(0.6, model, st_frame, image) else: # 影片讀取完畢,釋放資源 vid_cap.release() break except Exception as e: # 若過程中出錯(如影片無法讀取),顯示錯誤訊息 st.error(f'載入影片檔錯誤: {e}') ### 3-33 ### (streamlit_313) >>> python runApp.py ### 附錄 ### # Step 1:安裝及更新 conda >>> python -V # 檢查目前 Python 版本 >>> pip list # 檢查目前 Python 環境安裝了那些 Package >>> conda -V # 檢查目前 conda 版本 >>> conda -V # 檢查目前 conda 環境安裝了那些 Package >>> conda update conda # 若想要進行更新,可以輸入下列命令 (更新必須以系統管理員執行 Anaconda Prompt) # Step 2:建立虛擬環境 >>> conda env list # 看目前系統已經安裝幾個虛擬環境 >>> conda create --name my_313 python=3.13 # 若要建立一個叫做 my_313 的虛擬環境,並且指定最新的 Python 的版本 >>> conda create --name my_27 python=2.7 # 若要建立一個叫做 my_27 的虛擬環境,並且指定 Python 2.7 的版本 >>> conda env list # 再次檢查目前虛擬環境 # Step 3:啟動虛擬環境 >>> activate my_27 # 啟動一個新的虛擬環境 (my_27) >>> conda list # 檢查目前此虛擬環境中的 Conda 安裝了那些東西 (my_27) >>> pip list # 檢查目前虛擬環境的 Python 環境,安裝了那些 Package (my_27) >>> pip install numpy # 要在此虛擬環境下安裝所需套件,例如:numpy # Step 4:離開虛擬環境 (my_27) >>> deactivate # 關閉目前的虛擬環境 # Step 5:刪除虛擬環境的 Package 或虛擬環境本身 (my_27) >>> pip uninstall numpy # 要刪除虛擬環境中某個 Package。(例如:虛擬環境 my_27 中的 numpy) (my_27) >>> deactivate # 關閉目前的虛擬環境 >>> conda env remove --name my_27 # 要刪除整個虛擬環境 (不能刪除當前的虛擬環境,必須先關閉目前的虛擬環境) # Step 6:複製虛擬環境 >>> conda create -n my_new313 --clone my_313 # 複製虛擬環境 my_313,新的虛擬環境為 my_new313 # Step 7:為虛擬環境安裝 Anaconda >>> conda create --name my_313 python=3.13 & activate my_313 # 建一個 Python 3.13 的虛擬環境 my_313,並且切換至 my_313 (my_313) >>> conda install anaconda # 安裝 Anaconda